#!/usr/bin/env node

import { build } from 'esbuild';
import { rimraf } from 'rimraf';
import { join } from 'path';
import { fileURLToPath } from 'url';
import { getPackagesSync } from '@mj-pro/node-utils';
import process from 'process';

const __filename = fileURLToPath(import.meta.url);
const __dirname = join(__filename, '..');

let validPackages = [];

async function cleanDist() {
  console.warn('🧹 清理构建目录...');
  await rimraf('dist');
}

const getValidPackages = async () => {
  if (!validPackages.length) {
    const result = getPackagesSync();
    const path = await import('path');
    validPackages = (result.packages || []).filter(
      (pkg) =>
        pkg.packageJson.name &&
        pkg.packageJson.name.startsWith('@mj-pro/') &&
        !pkg.dir.includes(`${path.sep}apps${path.sep}`),
    );
  }
  return validPackages;
};

async function getWorkspacePackages() {
  try {
    const packageMap = new Map();
    const validPackages = await getValidPackages();
    validPackages.forEach((pkg) => {
      packageMap.set(pkg.packageJson.name, {
        path: pkg.dir,
        packageJson: pkg.packageJson,
      });
    });

    return packageMap;
  } catch (error) {
    console.warn('⚠️ 无法获取工作区包信息:', error.message);
    return new Map();
  }
}

async function isUtilityPackage(packageName) {
  const validPackages = await getValidPackages();
  return validPackages.some((pkg) => pkg.packageJson.name === packageName);
}

async function findEntryFile(packagePath, packageInfo) {
  const fs = await import('fs');

  const getEntryFromPackageJson = () => {
    if (
      packageInfo.packageJson.main &&
      !packageInfo.packageJson.main.includes('dist/')
    ) {
      return packageInfo.packageJson.main;
    }

    if (packageInfo.packageJson.exports) {
      const exports = packageInfo.packageJson.exports;
      if (typeof exports === 'string') {
        return exports;
      }
      if (exports['.'] && typeof exports['.'] === 'string') {
        return exports['.'];
      }
      if (exports['./index'] && typeof exports['./index'] === 'string') {
        return exports['./index'];
      }
    }

    return null;
  };

  const entryFile = getEntryFromPackageJson() || 'src/index.ts';
  const entry = join(packagePath, entryFile);

  if (fs.existsSync(entry)) {
    return entry;
  }

  const commonEntries = [
    'src/index.ts',
    'src/index.js',
    'index.ts',
    'index.js',
    'lib/index.js',
    'dist/index.js',
  ];
  for (const commonEntry of commonEntries) {
    const testEntry = join(packagePath, commonEntry);
    if (fs.existsSync(testEntry)) {
      return testEntry;
    }
  }

  return null;
}

async function resolveWorkspacePackage(packageName, packageMap) {
  const packageInfo = packageMap.get(packageName);
  if (!packageInfo) {
    console.warn(`⚠️ 工作区包 ${packageName} 不存在`);
    return { path: packageName, external: true };
  }

  if (await isUtilityPackage(packageName)) {
    const entryFile = await findEntryFile(packageInfo.path, packageInfo);
    if (entryFile) {
      return { path: entryFile };
    }

    if (packageInfo.packageJson.main) {
      const mainEntry = join(packageInfo.path, packageInfo.packageJson.main);
      if ((await import('fs')).existsSync(mainEntry)) {
        return { path: mainEntry };
      }
    }

    console.warn(
      `⚠️ 无法解析工作区包 ${packageName} 的入口文件，标记为外部依赖`,
    );
    return { path: packageName, external: true };
  }

  return { path: packageName, external: true };
}

async function setWorkspacePackages(productionPackageJson, packageInfo) {
  const fs = await import('fs');
  const packageJsonPath = join(packageInfo.path, 'package.json');
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));

  const workspacePackages = await getWorkspacePackages();

  // 使用 for...of 替代 forEach 来正确处理异步操作
  for (const [dep, version] of Object.entries(packageJson.dependencies || {})) {
    if (!workspacePackages.get(dep)) {
      if (
        productionPackageJson.dependencies[dep] &&
        version !== productionPackageJson.dependencies[dep]
      ) {
        console.warn(`⚠️ 工作区包 ${dep} 的版本不一致`);
      }
      productionPackageJson.dependencies[dep] = version;
    } else {
      await setWorkspacePackages(
        productionPackageJson,
        workspacePackages.get(dep),
      );
    }
  }
}

async function generateProductionPackageJson() {
  try {
    const fs = await import('fs');
    const packageJsonPath = join(__dirname, '..', 'package.json');
    const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));

    const productionPackageJson = {
      name: packageJson.name,
      version: packageJson.version,
      description: packageJson.description,
      main: packageJson.main,
      module: packageJson.module,
      type: packageJson.type,
      exports: packageJson.exports,
      files: packageJson.files,
      scripts: {
        start: 'node dist/index.cjs',
        'start:esm': 'node dist/index.mjs',
      },
      dependencies: {},
      engines: packageJson.engines || { node: '>=20.0.0' },
    };

    const workspacePackages = await getWorkspacePackages();

    // 使用 for...of 替代 forEach 来正确处理异步操作
    for (const [dep, version] of Object.entries(
      packageJson.dependencies || {},
    )) {
      if (!workspacePackages.get(dep)) {
        productionPackageJson.dependencies[dep] = version;
      } else {
        await setWorkspacePackages(
          productionPackageJson,
          workspacePackages.get(dep),
        );
      }
    }

    const productionPackageJsonPath = join(
      __dirname,
      '..',
      'dist',
      'package.json',
    );
    fs.writeFileSync(
      productionPackageJsonPath,
      JSON.stringify(productionPackageJson, null, 2),
    );
    console.warn('✅ 生产环境package.json生成完成');
  } catch (error) {
    console.error('❌ 生成生产环境package.json失败:', error);
  }
}

const createCjsConfig = (packageMap) => ({
  entryPoints: ['src/index.ts'],
  bundle: true,
  platform: 'node',
  target: 'node20',
  format: 'cjs',
  outfile: 'dist/index.cjs',
  minify: true,
  sourcemap: false,
  external: [
    'os',
    'fs',
    'path',
    'url',
    'http',
    'https',
    'stream',
    'util',
    'events',
    'buffer',
    'crypto',
    'querystring',
    'zlib',
    'readline',
    'child_process',
    'cluster',
    'dgram',
    'dns',
    'domain',
    'module',
    'net',
    'punycode',
    'repl',
    'string_decoder',
    'tls',
    'tty',
    'vm',
    'worker_threads',
  ],
  define: { 'process.env.NODE_ENV': '"production"' },
  treeShaking: true,
  keepNames: false,
  metafile: false,
  nodePaths: [join(__dirname, '..', 'node_modules')],
  plugins: [
    {
      name: 'workspace-resolver',
      setup(build) {
        build.onResolve({ filter: /^node:/ }, (args) => {
          const moduleName = args.path.replace('node:', '');
          return { path: moduleName, external: true };
        });

        build.onResolve({ filter: /^@mj-pro\// }, async (args) => {
          const packageName = args.path;
          const result = await resolveWorkspacePackage(packageName, packageMap);
          return result;
        });

        build.onResolve(
          {
            filter:
              /^(pg-hstore|pg|sqlite3|tedious|mysql2|mysql|oracledb|mssql)$/,
          },
          (args) => ({ path: args.path, external: true }),
        );
      },
    },
  ],
});

async function buildTarget(config, name) {
  console.warn(`🚀 开始构建 ${name}...`);
  try {
    await build(config);
    console.warn(`✅ ${name} 构建完成`);
  } catch (error) {
    console.error(`❌ ${name} 构建失败:`, error);
    throw error;
  }
}

async function installDependencies() {
  try {
    console.warn('📦 正在自动安装依赖...');
    const distPath = join(__dirname, '..', 'dist');
    const { execSync } = await import('child_process');

    let shell;
    if (process.platform === 'win32') {
      shell = 'powershell.exe';
    } else {
      try {
        const fs = await import('fs');
        shell = fs.existsSync('/bin/sh')
          ? '/bin/sh'
          : fs.existsSync('/bin/bash')
            ? '/bin/bash'
            : undefined;
      } catch {
        shell = undefined;
      }
    }

    // 使用 --prefer-offline 避免缓存警告
    execSync('npm install --prefer-offline', {
      cwd: distPath,
      stdio: 'inherit',
      shell,
    });
    console.warn('✅ 依赖安装完成！');
  } catch (error) {
    console.warn('⚠️ 依赖安装失败:', error.message);
    console.warn('💡 请手动在dist目录下运行: npm install --prefer-offline');
  }
}

async function main() {
  try {
    const packageMap = await getWorkspacePackages();
    console.warn(`✅ 找到 ${packageMap.size} 个工作区包`);

    await cleanDist();

    const cjsConfig = createCjsConfig(packageMap);
    await buildTarget(cjsConfig, 'CommonJS版本');

    await generateProductionPackageJson();
    await installDependencies();

    console.warn('\n🎉 构建任务完成！');
    console.warn('🔍 工作区依赖已深度解析并一起打包，确保运行时一致性');
  } catch (error) {
    console.error('💥 构建失败:', error);
    process.exit(1);
  }
}

main();
